package com.powerband.apiheartrate.ota

import android.util.Log
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class BleOtaManager(private var bleOtaCallback: BleOtaCallback) {

    val TAG = "SYD OTA"

    //var m_dir = Environment.getExternalStorageDirectory().path
    private var ReadData: ByteArray? = null
    private var SendPacketID = 0
    private var SendPacketAllNum = 0
    private var SendSectionID = 0
    private var CRC = 0
    private var SECTION_CRC = 0

    private val CMD_FW_WRITE_START: Byte = 0x14

    private val CMD_FW_ERASE: Byte = 0x16
    private val CMD_FW_UPGRADEV20: Byte = 0x15
    private val ERR_COMMAND_FAILED: Byte = 0x01
    private val EVT_COMMAND_COMPLETE = 0x0E.toByte()

    private val MAX_TRANS_COUNT_V30 = 20
    private val MAX_TRANS_SECTIONALL_COUNT = 5120
    private val MAX_TRANS_SECTIONALL_PACKET_COUNT = MAX_TRANS_SECTIONALL_COUNT / 20
    private var MAX_TRANS_SECTIONALL_SIZE = 0

    private var checkCRC = false

    private var actionType = 0
    private val ACTIONTYPE_CMD_FW_WRITE_START = 0x01
    private val ACTIONTYPE_CMD_FW_WRITE_END = 0x02
    private val ACTIONTYPE_CMD_FW_ERASE = 0x03
    private val ACTIONTYPE_CMD_FW_UPGRADE = 0x05
    private val ACTIONTYPE_CMD_FW_FINISH = 0x09

    private fun clearAll() {
        SendPacketID = 0
        SendPacketAllNum = 0
        SendSectionID = 0
        CRC = 0
        SECTION_CRC = 0
        MAX_TRANS_SECTIONALL_SIZE = 0
        checkCRC = false
        actionType = 0
    }

    fun bleDidWriteValue(data: ByteArray?) {
        //是否去读
        if (checkCRC) {
            Log.i(
                "SYD_OTA",
                "Read PacketID:$SendPacketID  AllNum:$SendPacketAllNum"
            )
            MainScope().launch {
                delay(800)
                bleOtaCallback.readOtaChar()
            }
        } else {
            Log.i(
                "SYD_OTA",
                "Receive PacketID:$SendPacketID  AllNum:$SendPacketAllNum"
            )
            OTA_Process_doing(data)
        }
    }

    fun bleDidReadValue(data: ByteArray?) {
        Log.i("SYD_OTA", "ACTION_DATA_READ")
        if (checkCRC) {
            OTA_Process_doing(data)
        }
    }


    fun startOTA(data: ByteArray?) {
        clearAll()
        ReadData = data
        if (ReadData == null) {
            //Toast.makeText(this@OTA_Active, "文件太小，请重新选择！", Toast.LENGTH_SHORT).show()
            bleOtaCallback.otaError(OtaError.InvalidFile)
            return
        }
        OTA_Process_Start()
    }

    private fun OTA_Process_init() {
        SendPacketID = 0
        SendPacketAllNum = 0
        CRC = 0
        ReadData = null
        actionType = 0
        SendSectionID = 0
        SECTION_CRC = 0
        MAX_TRANS_SECTIONALL_SIZE = 0
    }

    private fun OTA_Process_doing(data: ByteArray?) {
        when (actionType) {
            ACTIONTYPE_CMD_FW_ERASE -> {
                MainScope().launch {
                    delay(3000)
                    OTA_Write_Flash_Start_V30(data)
                }
            }
            ACTIONTYPE_CMD_FW_WRITE_START -> OTA_Write_Section_All_Flash()
            ACTIONTYPE_CMD_FW_WRITE_END -> OTA_Write_Flash_Continue_V30(data)
            ACTIONTYPE_CMD_FW_UPGRADE -> OTA_Upgrade_Flash_V30(ReadData!!.size, CRC)
            ACTIONTYPE_CMD_FW_FINISH -> {
                if ((data!![0].toInt() and 0xFF == EVT_COMMAND_COMPLETE.toInt()) and (data[3].toInt() and 0xFF == ERR_COMMAND_FAILED.toInt())) {
                    // ShowStatus("OTA失败, 请重新OTA")
                    bleOtaCallback.otaError(OtaError.Failed)
                } else {
                    //ShowStatus("OTA完成, 复位设备中")
                    bleOtaCallback.otaLog(OtaStatus.Finished, 1f)
                }
                OTA_Process_init()
            }
            else -> {}
        }
    }

    private fun OTA_Process_Start() {
        //ReadData = ReadOTAFileBinary(OTA_FilePath)
        var i = 0
        while (i < ReadData!!.size) {
            var CC = ReadData!![i].toInt()
            CC = CC and 0x000000FF
            CRC += CC
            CRC = CRC and 0x0000FFFF
            i++
        }
        Log.i("SYD_OTA", "OTA_Process_Start CRC ==>$CRC")
        OTA_Erase_Flash()
    }

    private fun OTA_Erase_Flash() {
        actionType = ACTIONTYPE_CMD_FW_ERASE
        val writeData = ByteArray(2)
        writeData[0] = CMD_FW_ERASE
        writeData[1] = 0x00
        Log.i("SYD_OTA", "OTA_Erase_Flash Start")
        bleOtaCallback.otaLog(OtaStatus.Erazing, 0f)
        bleOtaCallback.sendData(writeData)
    }

    private fun OTA_Write_Flash_Start_V30(data: ByteArray?) {
        Log.i("SYD_OTA", "OTA_Write_Flash_Start_V30")
        SendPacketAllNum = ReadData!!.size / MAX_TRANS_COUNT_V30
        if (ReadData!!.size % MAX_TRANS_COUNT_V30 != 0) SendPacketAllNum += 1
        OTA_Write_Flash_Continue_V30(data)
        bleOtaCallback.otaLog(OtaStatus.Start, 0f)
    }

    private fun OTA_Write_Flash_Continue_V30(data: ByteArray?) {
        actionType = ACTIONTYPE_CMD_FW_WRITE_START
        if (SendSectionID != 0 && data != null) {
            val check = data[7].toInt() and 0xff shl 8 or (data[6].toInt() and 0xff)
            //error check and resend
            if (check and 0x0000ffff != SECTION_CRC and 0x0000ffff) {
                Log.i(
                    "SYD_OTA",
                    "SECTION resend:$SendSectionID check device:$check check app:$SECTION_CRC"
                )
                bleOtaCallback.otaError(OtaError.ReSend)
                SendSectionID -= 1
                SendPacketID = MAX_TRANS_SECTIONALL_PACKET_COUNT * SendSectionID
            }
        }
        MAX_TRANS_SECTIONALL_SIZE =
            if (SendPacketAllNum - SendPacketID > MAX_TRANS_SECTIONALL_PACKET_COUNT) MAX_TRANS_SECTIONALL_COUNT else ReadData!!.size % MAX_TRANS_SECTIONALL_COUNT
        SECTION_CRC = 0
        for (i in 0 until MAX_TRANS_SECTIONALL_SIZE) {
            var CC = ReadData!![SendSectionID * MAX_TRANS_SECTIONALL_COUNT + i].toInt()
            CC = CC and 0x000000FF
            SECTION_CRC += CC
        }
        OTA_Write_Flash_section_start(SECTION_CRC, MAX_TRANS_SECTIONALL_SIZE, SendSectionID)
        SendSectionID += 1
        checkCRC = false
    }

    private fun OTA_Write_Section_All_Flash() {
        val srcPos = SendPacketID * MAX_TRANS_COUNT_V30
        val dataPacket = ByteArray(MAX_TRANS_COUNT_V30)
        if (SendPacketID == SendPacketAllNum) {
            actionType = 0
        } else {
            if (SendPacketID == SendPacketAllNum - 1) {
                System.arraycopy(
                    ReadData, srcPos, dataPacket, 0,
                    ReadData!!.size - srcPos
                ) //last a packet
                actionType = ACTIONTYPE_CMD_FW_UPGRADE //发送完最后一包了
            } else {
                System.arraycopy(
                    ReadData,
                    srcPos,
                    dataPacket,
                    0,
                    MAX_TRANS_COUNT_V30
                ) //other packet except first and last packet
            }
            OTA_Write_Secton_Flash(dataPacket)
            SendPacketID += 1
        }
        Log.i(
            "SYD_OTA",
            "SendPacketID%MAX_TRANS_SECTIONALL_PACKET_COUNT ==>" + SendPacketID % MAX_TRANS_SECTIONALL_PACKET_COUNT
        )
        if (actionType != ACTIONTYPE_CMD_FW_UPGRADE) {
            if (SendPacketID % MAX_TRANS_SECTIONALL_PACKET_COUNT == 0) {
                actionType = ACTIONTYPE_CMD_FW_WRITE_END
                checkCRC = true
                Log.i(
                    "SYD_OTA",
                    "Section:" + (SendPacketID / MAX_TRANS_SECTIONALL_PACKET_COUNT).toString()
                )
            }
        }
        val p = (SendPacketID.toFloat() / SendPacketAllNum.toFloat() * 100)

        bleOtaCallback.otaLog(if (p.toInt() >= 100) OtaStatus.Finished else OtaStatus.Inprogress, p)
    }

    private fun OTA_Write_Secton_Flash(ProgramData: ByteArray?) {
        if (ProgramData != null) {
            bleOtaCallback.sendData(ProgramData)
        }
    }


    private fun OTA_Write_Flash_section_start(check: Int, size: Int, Address: Int) {
        var address = Address
        val WriteData = ByteArray(10)
        address *= MAX_TRANS_SECTIONALL_COUNT
        WriteData[0] = CMD_FW_WRITE_START
        WriteData[1] = 0x13
        WriteData[2] = (address and 0x000000FF).toByte()
        WriteData[3] = (address and 0x0000FF00 shr 8).toByte()
        WriteData[4] = (address and 0x00FF0000 shr 16).toByte()
        WriteData[5] = (address and -0x1000000 shr 24).toByte()
        WriteData[6] = (size and 0x000000FF).toByte()
        WriteData[7] = (size and 0x0000FF00 shr 8).toByte()
        WriteData[8] = (check and 0x000000FF).toByte()
        WriteData[9] = (check and 0x0000FF00 shr 8).toByte()
        bleOtaCallback.sendData(WriteData) //写入第一包
    }


    private fun OTA_Upgrade_Flash_V30(Size: Int, CRC: Int) {
        Log.i("SYD_OTA", "OTA_Upgrade_Flash CRC_V30:" + CRC + "Size" + Size)
        val WriteData = ByteArray(8)
        WriteData[0] = CMD_FW_UPGRADEV20
        WriteData[1] = 0x04
        WriteData[2] = (Size and 0x000000FF).toByte()
        WriteData[3] = (Size and 0x0000FF00 shr 8).toByte()
        WriteData[4] = (Size and 0x00FF0000 shr 16).toByte()
        WriteData[5] = (Size and -0x1000000 shr 24).toByte()
        WriteData[6] = (CRC and 0x000000FF).toByte()
        WriteData[7] = (CRC and 0x0000FF00 shr 8).toByte()
        actionType = ACTIONTYPE_CMD_FW_FINISH.toInt()
        checkCRC = true
        bleOtaCallback.sendData(WriteData)
    }
}